package com.tsing.cedap.rest.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tsing.cedap.common.config.RedisManager;
import com.tsing.cedap.common.config.WebSocketManager;
import com.tsing.cedap.common.constants.AuthConstant;
import com.tsing.cedap.common.utils.GetLoginUserUtil;
import com.tsing.cedap.common.vo.BasePageVo;
import com.tsing.cedap.common.vo.PageResult;
import com.tsing.cedap.common.vo.UserVo;
import com.tsing.cedap.rest.entity.AccountEntity;
import com.tsing.cedap.rest.entity.ReceivingAddressEntity;
import com.tsing.cedap.rest.entity.WalletEntity;
import com.tsing.cedap.rest.mapper.AccountMapper;
import com.tsing.cedap.rest.mapper.ReceivingAddressMapper;
import com.tsing.cedap.rest.mapper.UserMapper;
import com.tsing.cedap.rest.mapper.WalletMapper;
import com.tsing.cedap.rest.service.IMiniAboutMeService;
import com.tsing.cedap.rest.vo.reqVo.CodeReqVo;
import com.tsing.cedap.rest.vo.reqVo.MiniUpdateAddressReqVo;
import com.tsing.cedap.rest.vo.reqVo.TradeReqVo;
import com.tsing.cedap.rest.vo.respVo.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @author TheTsing
 * @create 2022-01-27 9:41
 */
@Service
public class MiniAboutMeServiceImpl implements IMiniAboutMeService {

    @Autowired
    private RedisManager redisManager;

    @Resource
    private UserMapper userMapper;

    @Resource
    private WalletMapper walletMapper;

    @Resource
    private AccountMapper accountMapper;

    @Resource
    private ReceivingAddressMapper addressMapper;

    @Override
    public MiniGetUserInfoRespVo miniUserInfo(HttpServletRequest request) {
        String token = request.getHeader(AuthConstant.AUTHENTICATION_HEAD);
        UserVo userVo = GetLoginUserUtil.getLoginUserInfo(token, redisManager);
        return userMapper.miniUserInfo(userVo.getId());
    }

    @Override
    public MiniGetBalanceRespVo miniGetBalance(HttpServletRequest request) {
        String token = request.getHeader(AuthConstant.AUTHENTICATION_HEAD);
        UserVo userVo = GetLoginUserUtil.getLoginUserInfo(token, redisManager);
        WalletEntity walletEntity = new LambdaQueryChainWrapper<>(walletMapper)
                .select(WalletEntity::getBalance)
                .eq(WalletEntity::getUserId, userVo.getId())
                .last("LIMIT 1")
                .one();
        return new MiniGetBalanceRespVo()
                .setBalance(walletEntity.getBalance());
    }

    @Override
    public PageResult<MiniGetAccountPageRespVo> miniGetAccountPage(BasePageVo reqVo, HttpServletRequest request) {
        String token = request.getHeader(AuthConstant.AUTHENTICATION_HEAD);
        UserVo userVo = GetLoginUserUtil.getLoginUserInfo(token, redisManager);
        IPage<AccountEntity> pages = new Page<>(reqVo.getPageNum(), reqVo.getPageSize());
        new LambdaQueryChainWrapper<>(accountMapper)
                .select(AccountEntity::getAmount, AccountEntity::getType, AccountEntity::getDescription, AccountEntity::getCreateTime)
                .eq(AccountEntity::getUserId, userVo.getId())
                .orderByDesc(AccountEntity::getCreateTime)
                .page(pages);
        return new PageResult<>(pages, BeanUtil.copyToList(pages.getRecords(), MiniGetAccountPageRespVo.class));
    }

    @Override
    public PageResult<MiniGetAddressPageRespVo> miniGetAddressPage(BasePageVo reqVo, HttpServletRequest request) {
        String token = request.getHeader(AuthConstant.AUTHENTICATION_HEAD);
        UserVo userVo = GetLoginUserUtil.getLoginUserInfo(token, redisManager);
        IPage<MiniGetAddressPageRespVo> page = new Page<>(reqVo.getPageNum(), reqVo.getPageSize());
        return new PageResult<>(page, addressMapper.miniGetAddressPage(page, userVo.getId()));
    }

    @Override
    public MiniGetAddressByIdRespVo miniGetAddressById(CodeReqVo reqVo) {
        return addressMapper.miniGetAddressById(reqVo.getCode());
    }

    @Override
    public String miniAddAddress(MiniUpdateAddressReqVo reqVo, HttpServletRequest request) {
        ReceivingAddressEntity receivingAddressEntity = new ReceivingAddressEntity();
        BeanUtil.copyProperties(reqVo, receivingAddressEntity);
        String token = request.getHeader(AuthConstant.AUTHENTICATION_HEAD);
        UserVo userVo = GetLoginUserUtil.getLoginUserInfo(token, redisManager);
        receivingAddressEntity.setUserId(userVo.getId());
        addressMapper.insert(receivingAddressEntity);
        return receivingAddressEntity.getId();
    }

    @Override
    public void miniUpdateAddress(MiniUpdateAddressReqVo reqVo, HttpServletRequest request) {
        ReceivingAddressEntity receivingAddressEntity = new ReceivingAddressEntity();
        BeanUtil.copyProperties(reqVo, receivingAddressEntity);
        addressMapper.updateById(receivingAddressEntity);
    }

    @Value("${alipay.serverUrl}")
    private String serverUrl;

    @Value("${alipay.appId}")
    private String appId;

    @Value("${alipay.privateKey}")
    private String privateKey;

    @Value("${alipay.format}")
    private String format;

    @Value("${alipay.charset}")
    private String charset;

    @Value("${alipay.alipayPublicKey}")
    private String alipayPublicKey;

    @Value("${alipay.signType}")
    private String signType;

    @Value("${alipay.notifyUrl}")
    private String notifyUrl;

    @Value("${alipay.timeoutExpress}")
    private Integer timeoutExpress;

    @Value("${alipay.sysServiceProviderId}")
    private String sysServiceProviderId;

    @Override
    public String toPayAsPc(TradeReqVo reqVo, HttpServletRequest servletRequest) {
        // 创建AlipayClient实例
        AlipayClient alipayClient = new DefaultAlipayClient(serverUrl, appId, privateKey, format, charset, alipayPublicKey, signType);
        // 创建API对应的request(电脑网页版) 手机网页版 AlipayTradeWapPayRequest，电脑版 AlipayTradePagePayRequest
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        // 同步通知
        request.setReturnUrl(notifyUrl);
        Date date = new Date();
        String outTradeNo = DateUtil.format(date, "yyyyMMddHHmmss") + IdUtil.fastSimpleUUID();
        String timeExpire = DateUtil.offsetMinute(date, timeoutExpress).toString();
        // 填充业务参数
        request.setBizContent("{" +
                "    \"out_trade_no\":\"" + outTradeNo + "\"," +
                "    \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," +
                "    \"total_amount\":" + reqVo.getTotalAmount() + "," +
                "    \"subject\":\"" + reqVo.getSubject() + "\"," +
                "    \"body\":\"" + reqVo.getBody() + "\"," +
                "    \"time_expire\":\"" + timeExpire + "\"," +
                "    \"extend_params\":{" +
                "    \"sys_service_provider_id\":\"" + sysServiceProviderId + "\"" +
                "    }" +
                "    }");
        String response = null;
        try {
            // 调用SDK生成表单
            response = alipayClient.pageExecute(request, "GET").getBody();
        } catch (AlipayApiException e) {
        }
        String token = servletRequest.getHeader(AuthConstant.AUTHENTICATION_HEAD);
        redisManager.setString(outTradeNo, GetLoginUserUtil.getLoginUserInfo(token, redisManager).getId(), timeoutExpress * 60);
        return response;
    }

    @Override
    public String toPayAsPhone(TradeReqVo reqVo, HttpServletRequest servletRequest) {
        // 创建AlipayClient实例
        AlipayClient alipayClient = new DefaultAlipayClient(serverUrl, appId, privateKey, format, charset, alipayPublicKey, signType);
        // 创建API对应的request(电脑网页版) 手机网页版 AlipayTradeWapPayRequest，电脑版 AlipayTradePagePayRequest
        AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
        // 异步通知
        request.setReturnUrl(notifyUrl);
        Date date = new Date();
        String outTradeNo = DateUtil.format(date, "yyyyMMddHHmmss") + IdUtil.fastSimpleUUID();
        String timeExpire = DateUtil.offsetMinute(date, timeoutExpress).toString();
        // 填充业务参数
        request.setBizContent("{" +
                "    \"out_trade_no\":\"" + outTradeNo + "\"," +
                "    \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," +
                "    \"total_amount\":" + reqVo.getTotalAmount() + "," +
                "    \"subject\":\"" + reqVo.getSubject() + "\"," +
                "    \"body\":\"" + reqVo.getBody() + "\"," +
                "    \"time_expire\":\"" + timeExpire + "\"," +
                "    \"extend_params\":{" +
                "    \"sys_service_provider_id\":\"" + sysServiceProviderId + "\"" +
                "    }" +
                "    }");
        String response = null;
        try {
            // 调用SDK生成表单
            response = alipayClient.pageExecute(request, "GET").getBody();
        } catch (AlipayApiException e) {
        }
        String token = servletRequest.getHeader(AuthConstant.AUTHENTICATION_HEAD);
        redisManager.setString(outTradeNo, GetLoginUserUtil.getLoginUserInfo(token, redisManager).getId(), timeoutExpress * 60);
        return response;
    }

    private boolean rsaCheck(HttpServletRequest request) {
        Map<String, String> params = new HashMap<>(2);
        Map<String, String[]> requestParams = request.getParameterMap();
        Iterator<Map.Entry<String, String[]>> iterator = requestParams.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String[]> entry = iterator.next();
            String valueStr = "";
            for (String s : entry.getValue()) {
                valueStr += s;
            }
            params.put(entry.getKey(), valueStr);
        }
        try {
            return AlipaySignature.rsaCheckV1(params, alipayPublicKey, charset, signType);
        } catch (AlipayApiException e) {
            return false;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String aliPayNotify(HttpServletRequest request) {
        if (rsaCheck(request)) {
            // 交易状态
            String tradeStatus = request.getParameter("trade_status");
            // 商户订单号
            String outTradeNo = request.getParameter("out_trade_no");
            // 支付宝交易号
            String tradeNo = request.getParameter("trade_no");
            // 付款金额
            String totalAmount = request.getParameter("total_amount");
            // 验证
            if ("TRADE_SUCCESS".equals(tradeStatus)) {
                String userId = redisManager.getString(outTradeNo);
                if (StrUtil.isNotBlank(userId)) {
                    WalletEntity walletEntity = new LambdaQueryChainWrapper<>(walletMapper)
                            .select(WalletEntity::getId, WalletEntity::getBalance)
                            .eq(WalletEntity::getUserId, userId)
                            .last("LIMIT 1")
                            .one();
                    if (ObjectUtil.isNotNull(walletEntity)) {
                        WebSocketManager.sendInfo("TRADE_SUCCESS", userId);
                        walletMapper.updateById(walletEntity.setBalance(walletEntity.getBalance().add(new BigDecimal(totalAmount))));
                        accountMapper.insert(new AccountEntity()
                                .setUserId(userId)
                                .setAmount(new BigDecimal(totalAmount))
                                .setType(3)
                                .setDescription("使用支付宝充值 " + totalAmount + " 元，余额 " + walletEntity.getBalance() + " 元，支付宝交易号：" + tradeNo)
                                .setCreateTime(new Date()));
                        redisManager.deleteString(outTradeNo);
                    }
                }
                return "success";
            }
        }
        return "error";
    }

}
